using System;
using System.Collections;
using System.IO;
using System.Text;
using System.Xml;
using Altova.IO;
using Altova.TypeInfo;
using Altova.Types;

namespace Altova.Xml
{
	public class XmlTreeOperations
	{
		public class AllIterator : IEnumerable, IDisposable
		{
			private XmlNodeList list;

			public AllIterator(XmlNodeList list)
			{
				this.list = list;
			}

			public IEnumerator GetEnumerator()
			{
				return list.GetEnumerator();
			}

			public void Dispose()
			{
			}
		}

		public class MemberIterator : IEnumerable, IDisposable
		{
			private class Enumerator : IEnumerator
			{
				private IEnumerator iterator;

				private MemberInfo info;

				public object Current
				{
					get
					{
						return iterator.Current;
					}
				}

				public Enumerator(AllIterator list, MemberInfo info)
				{
					iterator = list.GetEnumerator();
					this.info = info;
				}

				public void Reset()
				{
					iterator.Reset();
				}

				public bool MoveNext()
				{
					while (iterator.MoveNext())
					{
						if (IsMember((XmlNode)iterator.Current, info))
						{
							return true;
						}
					}
					return false;
				}
			}

			private AllIterator iterator;

			private MemberInfo memberInfo;

			public MemberIterator(XmlNodeList list, MemberInfo info)
			{
				iterator = new AllIterator(list);
				memberInfo = info;
			}

			public IEnumerator GetEnumerator()
			{
				return new Enumerator(iterator, memberInfo);
			}

			public void Dispose()
			{
				iterator.Dispose();
			}
		}

		public static void CopyAll(XmlNode src, XmlNode tgt)
		{
			foreach (XmlNode childNode in src.ChildNodes)
			{
				tgt.AppendChild(tgt.OwnerDocument.ImportNode(childNode, true));
			}
			foreach (XmlNode attribute in src.Attributes)
			{
				tgt.Attributes.Append((XmlAttribute)tgt.OwnerDocument.ImportNode(attribute, true));
			}
		}

		private static XmlFormatter GetFormatter(MemberInfo member)
		{
			if (member.DataType.Formatter != null)
			{
				return (XmlFormatter)member.DataType.Formatter;
			}
			return (XmlFormatter)Xs.AnySimpleTypeFormatter;
		}

		public static bool IsEqualString(string a, string b)
		{
			if (a == b)
			{
				return true;
			}
			if (a == null)
			{
				a = "";
			}
			if (b == null)
			{
				a = "";
			}
			return a == b;
		}

		public static bool IsMember(XmlNode node, MemberInfo member)
		{
			if (member.LocalName == "")
			{
				if (node.NodeType != XmlNodeType.Text)
				{
					return node.NodeType == XmlNodeType.CDATA;
				}
				return true;
			}
			if (node.NodeType != XmlNodeType.Element)
			{
				return false;
			}
			string text = ((node.NamespaceURI == null) ? "" : node.NamespaceURI);
			string text2 = ((node.LocalName == null) ? "" : node.LocalName);
			string text3 = ((member.NamespaceURI == null) ? "" : member.NamespaceURI);
			string text4 = ((member.LocalName == null) ? "" : member.LocalName);
			if ((member.Flags & MemberFlags.SpecialName) != 0)
			{
				return text == "";
			}
			if (text == text3 && text2 == text4)
			{
				return true;
			}
			return false;
		}

		public static XmlNode Dereference(XmlNode node)
		{
			XmlAttribute xmlAttribute = node.Attributes["href", ""];
			if (xmlAttribute == null)
			{
				return node;
			}
			string value = xmlAttribute.Value;
			if (!value.StartsWith("#"))
			{
				throw new InvalidOperationException("Cannot dereference external references.");
			}
			string text = value.Substring(1);
			XmlNode xmlNode = node.OwnerDocument.SelectSingleNode("//*[@id='" + text + "']");
			if (xmlNode != null)
			{
				return xmlNode;
			}
			return node;
		}

		public static bool Exists(XmlNode node)
		{
			return node != null;
		}

		public static AllIterator GetElements(XmlNode node)
		{
			return new AllIterator(node.ChildNodes);
		}

		public static MemberIterator GetElements(XmlNode node, MemberInfo member)
		{
			return new MemberIterator(node.ChildNodes, member);
		}

		public static void SetTextValue(XmlNode node, string value)
		{
			node.InnerXml = "";
			if (value != "")
			{
				node.InnerText = value;
			}
		}

		public static string GetTextValue(XmlNode node)
		{
			return node.InnerText;
		}

		private static string FindPrefixForNamespace(XmlNode node, string uri)
		{
			if (uri == "http://www.w3.org/XML/1998/namespace")
			{
				return "xml";
			}
			return node.GetPrefixOfNamespace(uri);
		}

		private static string FindUnusedPrefix(XmlNode node)
		{
			return FindUnusedPrefix(node, null);
		}

		public static string FindUnusedPrefix(XmlNode node, string prefixHint)
		{
			string text = prefixHint;
			if (prefixHint == null || prefixHint.Length == 0)
			{
				text = "n";
			}
			else
			{
				string namespaceOfPrefix = node.GetNamespaceOfPrefix(text);
				if (namespaceOfPrefix == null || namespaceOfPrefix.Length == 0)
				{
					return text;
				}
			}
			int num = 1;
			string text2;
			string namespaceOfPrefix2;
			do
			{
				text2 = string.Format(text + "{0}", num++);
				namespaceOfPrefix2 = node.GetNamespaceOfPrefix(text2);
			}
			while (namespaceOfPrefix2 != null && namespaceOfPrefix2.Length != 0);
			return text2;
		}

		public static void SetAttribute(XmlNode node, string localName, string namespaceURI, string value)
		{
			XmlAttribute xmlAttribute = node.OwnerDocument.CreateAttribute(localName, namespaceURI);
			xmlAttribute.Value = value;
			node.Attributes.Append(xmlAttribute);
		}

		public static void SetAttribute(XmlNode node, string localName, string namespaceURI, XmlQualifiedName value)
		{
			XmlAttribute xmlAttribute = node.OwnerDocument.CreateAttribute(localName, namespaceURI);
			if (value.Namespace == null || value.Namespace == "")
			{
				xmlAttribute.Value = value.Name;
			}
			else
			{
				string text = FindPrefixForNamespace(node, value.Namespace);
				if (text == null || text == "")
				{
					text = FindUnusedPrefix(node);
					XmlAttribute xmlAttribute2 = node.OwnerDocument.CreateAttribute("xmlns", text, "http://www.w3.org/2000/xmlns/");
					xmlAttribute2.Value = value.Namespace;
					node.Attributes.Append(xmlAttribute2);
				}
				xmlAttribute.Value = text + ":" + value.Name;
			}
			node.Attributes.Append(xmlAttribute);
		}

		public static void SetValue(XmlNode node, MemberInfo member, string value)
		{
			if (member.LocalName != "")
			{
				string text = "";
				if (member.NamespaceURI != "")
				{
					text = FindPrefixForNamespace(node, member.NamespaceURI);
					text = ((text.Length <= 0) ? (FindUnusedPrefix(node) + ":") : (text + ":"));
				}
				XmlElement xmlElement = (XmlElement)node;
				XmlAttribute xmlAttribute = node.OwnerDocument.CreateAttribute(text + member.LocalName, member.NamespaceURI);
				xmlAttribute.Value = value;
				xmlElement.SetAttributeNode(xmlAttribute);
			}
			else
			{
				SetTextValue(node, value);
			}
		}

		public static void SetValue(XmlNode node, MemberInfo member, bool b)
		{
			SetValue(node, member, GetFormatter(member).Format(b));
		}

		public static void SetValue(XmlNode node, MemberInfo member, int b)
		{
			SetValue(node, member, GetFormatter(member).Format(b));
		}

		public static void SetValue(XmlNode node, MemberInfo member, uint b)
		{
			SetValue(node, member, GetFormatter(member).Format(b));
		}

		public static void SetValue(XmlNode node, MemberInfo member, long b)
		{
			SetValue(node, member, GetFormatter(member).Format(b));
		}

		public static void SetValue(XmlNode node, MemberInfo member, ulong b)
		{
			SetValue(node, member, GetFormatter(member).Format(b));
		}

		public static void SetValue(XmlNode node, MemberInfo member, double b)
		{
			SetValue(node, member, GetFormatter(member).Format(b));
		}

		public static void SetValue(XmlNode node, MemberInfo member, Altova.Types.DateTime b)
		{
			SetValue(node, member, GetFormatter(member).Format(b));
		}

		public static void SetValue(XmlNode node, MemberInfo member, Duration b)
		{
			SetValue(node, member, GetFormatter(member).Format(b));
		}

		public static void SetValue(XmlNode node, MemberInfo member, byte[] b)
		{
			SetValue(node, member, GetFormatter(member).Format(b));
		}

		public static void SetValue(XmlNode node, MemberInfo member, decimal d)
		{
			SetValue(node, member, GetFormatter(member).Format(d));
		}

		public static void SetValue(XmlNode node, MemberInfo member, QName qn)
		{
			if (qn.Uri == null)
			{
				SetValue(node, member, qn.LocalName);
				return;
			}
			string text = node.GetPrefixOfNamespace(qn.Uri);
			if (text == null || text.Length == 0)
			{
				text = FindUnusedPrefix(node, qn.Prefix);
				((XmlElement)node).SetAttribute("xmlns:" + text, qn.Uri);
			}
			SetValue(node, member, text + ":" + qn.LocalName);
		}

		public static XmlNode AddElement(XmlNode node, MemberInfo member)
		{
			string text = "";
			if (member.NamespaceURI != null)
			{
				text = FindPrefixForNamespace(node, member.NamespaceURI);
				if (text.Length > 0)
				{
					text += ":";
				}
			}
			XmlDocument xmlDocument = node.OwnerDocument;
			if (xmlDocument == null)
			{
				xmlDocument = (XmlDocument)node;
			}
			XmlNode xmlNode = xmlDocument.CreateElement(text + member.LocalName, member.NamespaceURI);
			node.AppendChild(xmlNode);
			return xmlNode;
		}

		public static double CastToDouble(XmlNode node, MemberInfo member)
		{
			return CoreTypes.CastToDouble(node.InnerText);
		}

		public static string CastToString(XmlNode node, MemberInfo member)
		{
			return CoreTypes.CastToString(node.InnerText);
		}

		public static long CastToInt64(XmlNode node, MemberInfo member)
		{
			return CoreTypes.CastToInt64(node.InnerText);
		}

		public static ulong CastToUInt64(XmlNode node, MemberInfo member)
		{
			return CoreTypes.CastToUInt64(node.InnerText);
		}

		public static uint CastToUInt(XmlNode node, MemberInfo member)
		{
			return CoreTypes.CastToUInt(node.InnerText);
		}

		public static int CastToInt(XmlNode node, MemberInfo member)
		{
			return CoreTypes.CastToInt(node.InnerText);
		}

		public static bool CastToBool(XmlNode node, MemberInfo member)
		{
			return CoreTypes.CastToBool(node.InnerText);
		}

		public static Altova.Types.DateTime CastToDateTime(XmlNode node, MemberInfo member)
		{
			return CoreTypes.CastToDateTime(node.InnerText);
		}

		public static Duration CastToDuration(XmlNode node, MemberInfo member)
		{
			return CoreTypes.CastToDuration(node.InnerText);
		}

		public static byte[] CastToBinary(XmlNode node, MemberInfo member)
		{
			return GetFormatter(member).ParseBinary(node.InnerText);
		}

		public static decimal CastToDecimal(XmlNode node, MemberInfo member)
		{
			return CoreTypes.CastToDecimal(node.InnerText);
		}

		public static QName CastToQName(XmlNode node, MemberInfo member)
		{
			int num = node.InnerText.IndexOf(":");
			if (num == -1)
			{
				return new QName(node.GetNamespaceOfPrefix(""), node.InnerText);
			}
			string prefix = node.InnerText.Substring(0, num);
			string local = node.InnerText.Substring(num + 1);
			string namespaceOfPrefix = node.GetNamespaceOfPrefix(prefix);
			return new QName(namespaceOfPrefix, prefix, local);
		}

		public static XmlNode FindAttribute(XmlNode node, MemberInfo member)
		{
			XmlElement xmlElement = (XmlElement)node;
			XmlAttributeCollection attributes = xmlElement.Attributes;
			return attributes.GetNamedItem(member.LocalName, member.NamespaceURI);
		}

		public static XmlDocument LoadDocument(Input input)
		{
			if (input is FileInput)
			{
				return LoadDocument(((FileInput)input).Filename);
			}
			switch (input.Type)
			{
			case Input.InputType.Stream:
				return LoadDocument(input.Stream);
			case Input.InputType.Reader:
				return LoadDocument(input.Reader);
			case Input.InputType.XmlDocument:
				return input.Document;
			default:
				throw new Exception("Unknown input type");
			}
		}

		public static XmlDocument LoadDocument(TextReader reader)
		{
			XmlDocument xmlDocument = new XmlDocument();
			xmlDocument.Load(reader);
			return xmlDocument;
		}

		public static XmlDocument LoadDocument(string filename)
		{
			XmlDocument xmlDocument = new XmlDocument();
			xmlDocument.Load(filename);
			return xmlDocument;
		}

		public static XmlDocument LoadXmlBinary(byte[] xmlTree)
		{
			MemoryStream inStream = new MemoryStream(xmlTree);
			XmlDocument xmlDocument = new XmlDocument();
			xmlDocument.Load(inStream);
			return xmlDocument;
		}

		public static XmlDocument LoadXml(string xmlString)
		{
			XmlDocument xmlDocument = new XmlDocument();
			xmlDocument.LoadXml(xmlString);
			return xmlDocument;
		}

		public static XmlDocument LoadDocument(Stream stream)
		{
			XmlDocument xmlDocument = new XmlDocument();
			xmlDocument.Load(stream);
			return xmlDocument;
		}

		public static void SaveDocument(XmlDocument doc, Output output, string encoding, bool bBigEndian, bool bBOM, bool prettyPrint)
		{
			SaveDocument(doc, output, GetEncodingObject(encoding, bBigEndian, bBOM), prettyPrint);
		}

		public static void SaveDocument(XmlDocument doc, Output output, Encoding encoding, bool prettyPrint)
		{
			switch (output.Type)
			{
			case Output.OutputType.Stream:
				SaveDocument(doc, output.Stream, encoding, prettyPrint);
				break;
			case Output.OutputType.Writer:
				SaveDocument(doc, output.Writer, prettyPrint);
				break;
			default:
				throw new Exception("Unknown output type");
			case Output.OutputType.XmlDocument:
				break;
			}
		}

		public static void SaveDocument(XmlDocument doc, string filename, bool prettyPrint)
		{
			SaveDocument(doc, filename, "UTF-8", false, false, prettyPrint);
		}

		public static void SaveDocument(XmlDocument doc, string filename, string encoding, bool bBigEndian, bool bBOM, bool prettyPrint)
		{
			SaveDocument(doc, filename, GetEncodingObject(encoding, bBigEndian, bBOM), prettyPrint);
		}

		public static void SaveDocument(XmlDocument doc, string filename, string encoding, bool prettyPrint)
		{
			SaveDocument(doc, filename, Encoding.GetEncoding(encoding), prettyPrint);
		}

		public static void SaveDocument(XmlDocument doc, Stream stream, bool prettyPrint)
		{
			SaveDocument(doc, stream, "UTF-8", false, false, prettyPrint);
		}

		public static void SaveDocument(XmlDocument doc, Stream stream, string encoding, bool bBigEndian, bool bBOM, bool prettyPrint)
		{
			SaveDocument(doc, stream, GetEncodingObject(encoding, bBigEndian, bBOM), prettyPrint);
		}

		public static void SaveDocument(XmlDocument doc, Stream stream, string encoding, bool prettyPrint)
		{
			SaveDocument(doc, stream, Encoding.GetEncoding(encoding), prettyPrint);
		}

		public static void SaveDocument(XmlDocument doc, Stream stream, Encoding encoding, bool prettyPrint)
		{
			SaveDocument(doc, new XmlTextWriter(stream, encoding), prettyPrint);
		}

		public static void SaveDocument(XmlDocument doc, TextWriter writer, bool prettyPrint)
		{
			SaveDocument(doc, new XmlTextWriter(writer), prettyPrint);
		}

		private static void SaveDocument(XmlDocument doc, XmlTextWriter xmlWriter, bool prettyPrint)
		{
			if (prettyPrint)
			{
				xmlWriter.Formatting = Formatting.Indented;
				xmlWriter.IndentChar = '\t';
				xmlWriter.Indentation = 1;
			}
			else
			{
				xmlWriter.Formatting = Formatting.None;
			}
			doc.Save(xmlWriter);
			xmlWriter.Flush();
		}

		public static void SaveDocument(XmlDocument doc, string filename, Encoding encoding, bool prettyPrint)
		{
			Stream stream = File.Create(filename);
			SaveDocument(doc, stream, encoding, prettyPrint);
			stream.Close();
		}

		public static byte[] SaveXmlBinary(XmlDocument doc, string encoding, bool bBigEndian, bool bBOM, bool prettyPrint)
		{
			return SaveXmlBinary(doc, GetEncodingObject(encoding, bBigEndian, bBOM), prettyPrint);
		}

		public static byte[] SaveXmlBinary(XmlDocument doc, string encoding, bool prettyPrint)
		{
			return SaveXmlBinary(doc, Encoding.GetEncoding(encoding), prettyPrint);
		}

		public static byte[] SaveXmlBinary(XmlDocument doc, Encoding encoding, bool prettyPrint)
		{
			MemoryStream memoryStream = new MemoryStream();
			XmlTextWriter xmlTextWriter = new XmlTextWriter(memoryStream, encoding);
			if (prettyPrint)
			{
				xmlTextWriter.Formatting = Formatting.Indented;
				xmlTextWriter.IndentChar = '\t';
				xmlTextWriter.Indentation = 1;
			}
			else
			{
				xmlTextWriter.Formatting = Formatting.None;
			}
			doc.Save(xmlTextWriter);
			xmlTextWriter.Close();
			return memoryStream.ToArray();
		}

		public static string SaveXml(XmlDocument doc, bool prettyPrint)
		{
			return SaveXml(doc, prettyPrint, false);
		}

		public static string SaveXml(XmlDocument doc, bool prettyPrint, bool omitXmlDec)
		{
			StringWriter stringWriter = new StringWriter();
			XmlTextWriter xmlTextWriter = new XmlTextWriter(stringWriter);
			if (prettyPrint)
			{
				xmlTextWriter.Formatting = Formatting.Indented;
				xmlTextWriter.IndentChar = '\t';
				xmlTextWriter.Indentation = 1;
			}
			else
			{
				xmlTextWriter.Formatting = Formatting.None;
			}
			doc.Save(xmlTextWriter);
			xmlTextWriter.Close();
			stringWriter.Close();
			string text = stringWriter.ToString();
			if (omitXmlDec)
			{
				int num = text.IndexOf("<?xml");
				if (num >= 0)
				{
					num = text.IndexOf('>', num);
					text = text.Substring(num + 1);
				}
			}
			return text;
		}

		private static Encoding GetEncodingObject(string encoding, bool bBigEndian, bool bBOM)
		{
			switch (GetUnicodeSizeFromEncodingName(encoding))
			{
			case 1:
				return new UTF8Encoding(bBOM);
			case 2:
				return new UTF16Encoding(bBigEndian, bBOM);
			default:
				return Encoding.GetEncoding(encoding);
			}
		}

		private static int GetUnicodeSizeFromEncodingName(string encoding)
		{
			if (encoding == null)
			{
				return 0;
			}
			encoding = encoding.ToUpper();
			if (encoding.IndexOf("UTF-8") >= 0)
			{
				return 1;
			}
			if (encoding.IndexOf("UTF-16") >= 0 || encoding.IndexOf("UCS-2") >= 0)
			{
				return 2;
			}
			return 0;
		}

		public static XmlDocument CreateDocument()
		{
			return new XmlDocument();
		}
	}
}
